package com.mask.share.utils;

import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;

import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;


/**
 * 通过openCV 进行图片处理
 */
public class ImageOpenCVUtils {


    static {
        loadLib();
    }


    /**
     * 读取图片（可以带中文）
     * 【注】一般用 Mat mat = Imgcodecs.imread(largeFile); 就可以了
     *
     * @param picPath
     * @return
     */
    public static Mat readImage(String picPath) {
        Mat image = new Mat();
        try {
            byte[] bytes = Files.readAllBytes(Paths.get(picPath));
            MatOfByte matOfByte = new MatOfByte(bytes);
            image = Imgcodecs.imdecode(matOfByte, Imgcodecs.IMREAD_COLOR);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return image;
    }

    /**
     * 使用openCV截取图片
     */
    public static String 截取图片(String filePath, int x, int y, int x1, int y1, String saveFile) {
        Mat src = ImageOpenCVUtils.readImage(filePath);
        // 定义截取区域（x, y, width, height）
        Rect rect = new Rect(x, y, x1 - x, y1 - y);
        // 截取图片
        Mat cropped = new Mat(src, rect);
        // 保存截取的图片
        Imgcodecs.imwrite(saveFile, cropped);
        // 释放资源
        src.release();
        cropped.release();
        return saveFile;
    }

    public static Mat 截取图片(Mat src, int x, int y, int x1, int y1) {
        // 定义截取区域（x, y, width, height）
        Rect rect = new Rect(x, y, x1 - x, y1 - y);
        // 截取图片
        Mat cropped = new Mat(src, rect);
        // 释放资源
        src.release();
        return cropped;
    }

    /**
     * 保存图片到硬盘
     */
    public static void saveMat(Mat mat, String path) {
        Imgcodecs.imwrite(path, mat);
    }
    /*================== 图片搜索 =====================*/

    public static void main(String[] args) {

//        GameImageUtils.searchImageAndClick(SceneEnum.任务栏_战, Cmd.找不到场景的不抛异常, Cmd.不要截图, Cmd.使用java比对图片);
        System.out.println("calculateHistogram  " + helpReadFile("D:\\adb\\pipei_sub_local.png", "D:\\adb\\pipei_sub_pad.png", ImageOpenCVUtils::calculateHistogram));
        System.out.println("calculateMSE " + helpReadFile("D:\\adb\\pipei_sub_local.png", "D:\\adb\\pipei_sub_pad.png", ImageOpenCVUtils::calculateMSE));
        System.out.println("calculatePSNR " + helpReadFile("D:\\adb\\pipei_sub_local.png", "D:\\adb\\pipei_sub_pad.png", ImageOpenCVUtils::calculatePSNR));
        System.out.println("calculateSSIM  " + helpReadFile("D:\\adb\\pipei_sub_local.png", "D:\\adb\\pipei_sub_pad.png", ImageOpenCVUtils::calculateSSIM));
    }

    /**
     * 计算图片相似度(直方图)
     */
    public static double calculateHistogram(Mat image1, Mat image2) {
        // 计算直方图
        Mat hist1 = calculateHistogram(image1);
        Mat hist2 = calculateHistogram(image2);

        // 计算相似度
        final double similarity = Imgproc.compareHist(hist1, hist2, Imgproc.CV_COMP_CORREL);

        // 手动释放内存
        if (hist1 != null) {
            hist1.release();
        }
        if (hist2 != null) {
            hist2.release();
        }

        return similarity;
    }

    private static Mat calculateHistogram(Mat image) {
        Mat hist = new Mat();

        // 设置直方图参数
        MatOfInt histSize = new MatOfInt(256);
        MatOfFloat ranges = new MatOfFloat(0, 256);
        MatOfInt channels = new MatOfInt(0);
        List<Mat> images = new ArrayList<>();
        images.add(image);

        // 计算直方图
        Imgproc.calcHist(images, channels, new Mat(), hist, histSize, ranges);

        return hist;
    }

    // 计算均方差（MSE）
    public static double calculateMSE(Mat image1, Mat image2) {
        Mat diff = new Mat();
        Core.absdiff(image1, image2, diff);
        Mat squaredDiff = new Mat();
        Core.multiply(diff, diff, squaredDiff);
        Scalar mseScalar = Core.mean(squaredDiff);
        return mseScalar.val[0];
    }

    // 计算结构相似性指数（SSIM）
    public static double calculateSSIM(Mat image1, Mat image2) {
        Mat image1Gray = new Mat();
        Mat image2Gray = new Mat();
        Imgproc.cvtColor(image1, image1Gray, Imgproc.COLOR_BGR2GRAY);
        Imgproc.cvtColor(image2, image2Gray, Imgproc.COLOR_BGR2GRAY);
        MatOfFloat ssimMat = new MatOfFloat();
        Imgproc.matchTemplate(image1Gray, image2Gray, ssimMat, Imgproc.CV_COMP_CORREL);
        Scalar ssimScalar = Core.mean(ssimMat);
        return ssimScalar.val[0];
    }

    // 计算峰值信噪比（PSNR）
    public static double calculatePSNR(Mat image1, Mat image2) {
        Mat diff = new Mat();
        Core.absdiff(image1, image2, diff);
        Mat squaredDiff = new Mat();
        Core.multiply(diff, diff, squaredDiff);
        Scalar mseScalar = Core.mean(squaredDiff);
        double mse = mseScalar.val[0];
        double psnr = 10.0 * Math.log10(255.0 * 255.0 / mse);
        return psnr;
    }

    /**
     * 从大图中搜索小图片
     *
     * @return 如果匹配到了，Point里面有位置信息。 目前看，一定会返回对象，但是不一定准，还需要校验
     */
    public static Point searchImage(String largeFile, String smallFile) {
        // 读取图片
        Mat largeMat = Imgcodecs.imread(largeFile);
        Mat smallMat = Imgcodecs.imread(smallFile);

        // 转换为灰度图
        Mat largeMatGray = new Mat();
        Mat smallMatGray = new Mat();
        Imgproc.cvtColor(largeMat, largeMatGray, Imgproc.COLOR_BGR2GRAY);
        Imgproc.cvtColor(smallMat, smallMatGray, Imgproc.COLOR_BGR2GRAY);

        // 执行模板匹配
        int matchMethod = Imgproc.TM_CCOEFF_NORMED;
        Mat result = new Mat();
        Imgproc.matchTemplate(largeMatGray, smallMatGray, result, matchMethod);

        // 归一化结果
        Core.normalize(result, result, 0, 1, Core.NORM_MINMAX, -1, new Mat());

        // 找到最佳匹配位置
        double minVal, maxVal;
        Point minLoc, maxLoc;
        Point matchLoc;

        Core.MinMaxLocResult mmr = Core.minMaxLoc(result);
        minVal = mmr.minVal;
        maxVal = mmr.maxVal;
        minLoc = mmr.minLoc;
        maxLoc = mmr.maxLoc;

        // 对于TM_CCOEFF_NORMED，最佳匹配是最大值
        matchLoc = maxLoc;

        return matchLoc;
    }

    public static <T> T helpReadFile(String file1, String file2, BiFunction<Mat, Mat, T> bf) {
        return bf.apply(readImage(file1), readImage(file2));
    }

    public static void loadLib() {
        System.setProperty("java.awt.headless", "false");
//        System.out.println(System.getProperty("java.library.path"));
        URL url = ClassLoader.getSystemResource("lib/opencv_java4100.dll");
        // 加载动态库
        System.load(url.getPath());
//        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
    }
}
